﻿using System;
using System.Text;
using EmbeddedSharp.Core;
using EmbeddedSharp.Core.Function.Arithmetics;
using EmbeddedSharp.Core.Function.Maths;

namespace EmbeddedSharp.Scripting.Interfacing.Mathematica {
    public class MathematicaExpressionTranslator : IExpressionTranslator, IVisitor {

        private string prefix = string.Empty;

        public MathematicaExpressionTranslator()
            : this(string.Empty) {

        }

        public MathematicaExpressionTranslator(string prefix) {
            this.prefix = prefix;
        }

        public string Translate(Node node) {
            return System.Convert.ToString(node.Accept(this));
        }

        #region IVisitor Members

        public object Visit(IdentifierNode node) {
            return string.IsNullOrEmpty(prefix)
                ? node.Value : prefix + "." + node.Value;
        }

        public object Visit(NumberNode node) {
            return node.Value;
        }

        public object Visit(SubNode node) {
            return this.VisitOperator(node);
        }

        public object Visit(AddNode node) {
            return this.VisitOperator(node);
        }

        public object Visit(MulNode node) {
            return this.VisitOperator(node);
        }

        public object Visit(ModNode node) {
            return this.VisitOperator(node);
        }

        public object Visit(DivNode node) {
            return this.VisitOperator(node);
        }

        public object VisitOperator(OperatorNode node) {
            StringBuilder buff = new StringBuilder();

            string l = Convert.ToString(node.Left.Accept(this));
            string r = Convert.ToString(node.Right.Accept(this));

            return buff.Append("(").Append(l).Append(node.Value).Append(r).Append(")").ToString();
        }

        public object Visit(PowerNode node) {
            StringBuilder buff = new StringBuilder();

            string l = Convert.ToString(node.Left.Accept(this));
            string r = Convert.ToString(node.Right.Accept(this));

            return buff.Append("Power[").Append(l).Append(",").Append(r).Append("]").ToString();
        }

        public object Visit(LogNode node) {
            StringBuilder buff = new StringBuilder();

            string l = Convert.ToString(node.Left.Accept(this));
            string r = Convert.ToString(node.Right.Accept(this));

            return buff.Append("Log[").Append(l).Append(",").Append(r).Append("]").ToString();
        }

        public object Visit(FuncNode node) {
            StringBuilder buff = new StringBuilder();

            bool first = true;
            buff.Append(GetFunctionName(node.Value)).Append("[");
            foreach (Node child in node.Parameters) {
                if (!first) {
                    buff.Append(",");
                }
                first = false;

                string t = Convert.ToString(child.Accept(this));
                buff.Append(t);
            }
            buff.Append("]");

            return buff.ToString();
        }

        public object Visit(StringNode node) {
            return node.Value;
        }

        #endregion

        public string GetFunctionName(string fName) {
            switch (fName) {
                case "cos":
                    return "Cos";
                case "sin":
                    return "Sin";
                case "acos":
                    return "ArcCos";
                case "asin":
                    return "ArcSin";
                default:
                    return fName;
            }
        }

        #region IVisitor Members

        public object VisitOperatorNode(OperatorNode node, string operatorName) {
            StringBuilder buff = new StringBuilder();

            string l = Convert.ToString(node.Left.Accept(this));
            string r = Convert.ToString(node.Right.Accept(this));

            return buff.Append("(").Append(l).Append(operatorName).Append(r).Append("]").ToString();
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.LargerThanNode node) {
            return this.VisitOperatorNode(node, ">");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.LessThanNode node) {
            return this.VisitOperatorNode(node, "<");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.LargerEqualToNode node) {
            return this.VisitOperatorNode(node, ">=");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.LessEqualToNode node) {
            return this.VisitOperatorNode(node, "<=");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.EqualNode node) {
            return this.VisitOperatorNode(node, "==");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.NotEqualNode node) {
            return this.VisitOperatorNode(node, "!=");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.AndNode node) {
            return this.VisitOperatorNode(node, "&&");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.OrNode node) {
            return this.VisitOperatorNode(node, "||");
        }

        public object Visit(global::EmbeddedSharp.Core.Function.Logic.NotNode node) {
            StringBuilder buff = new StringBuilder();

            string l = Convert.ToString(node.Operand.Accept(this));

            return buff.Append("(!").Append(l).Append(")").ToString();
        }

        #endregion
    }
}
